#include "Pixel.h"
#include "Pixel_r.h"
#include "Image_r.h"
#include "Image.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <crtdbg.h>

static void * Image_ctor(void * _pSelf, va_list * ppArgs)
{

	IMAGE * pSelf = base_ctor(Image, _pSelf, ppArgs);

	return pSelf;
}

static void * Image_dtor (void* _pSelf){
		IMAGE * pSelf = _pSelf;
		base_dtor(Image, _pSelf);
		return pSelf;
}


static IMAGE * CopyToImage(void *_pSelf , FILE *input){
	IMAGE *pSelf = _pSelf;
	BYTE r,g,b;
	int i,j;
	rewind(input);
	fseek(input,sizeof(BITMAPFILEHEADER)+ sizeof(BITMAPINFOHEADER),SEEK_SET);
	for (i = 0; i < pSelf->_.height; i++){
		for (j = 0; j < pSelf->_.width; j++){
			fread(&b,1,1,input);
			fread(&g,1,1,input);
			fread(&r,1,1,input);

			inplace_new(access(pSelf,j,i),Pixel,b,g,r);
			_CrtCheckMemory();
		}
	}
	return pSelf;
}

static void CopyToFile(void *_pSelf,FILE *output){
	IMAGE *pSelf = _pSelf;
	BYTE r,g,b;
	int i,j;
	rewind(output);
	fseek(output,sizeof(BITMAPFILEHEADER)+ sizeof(BITMAPINFOHEADER),SEEK_SET);
	for (i = 0; i < (int)pSelf->_.height; i++){
		for (j = 0; j < (int)pSelf->_.width; j++){
			b = ((PIXEL *)access(pSelf,j,i))->b;
			g = ((PIXEL *)access(pSelf,j,i))->g;
			r = ((PIXEL *)access(pSelf,j,i))->r;
			fwrite(&b,1,1,output);
			fwrite(&g,1,1,output);
			fwrite(&r,1,1,output);
		}
	}
}

static void * Image_read(void * _pSelf, va_list *pArgs ){
	const char *filename = va_arg(*pArgs,const char *);
	FILE *input;
	IMAGE *pSelf = _pSelf;
	BITMAPFILEHEADER fileheader;
	BITMAPINFOHEADER infoheader;
	input = fopen( filename, "rb" );
	if(input == NULL){
		printf("ppc");
		return NULL;
	}
	rewind(input);
	if(!fread(&fileheader,sizeof(BITMAPFILEHEADER),1,input)){
		printf( "Error reading.\n");
		return;
	}
	if(!fread(&infoheader,sizeof(BITMAPINFOHEADER),1,input)){
		printf( "Error reading.\n");
		return;
	}
	reload_ddarr(pSelf,sizeof(Pixel),infoheader.biWidth,infoheader.biHeight);
	pSelf = CopyToImage(pSelf,input);
	fclose(input);
	return pSelf;
}

void Image_write(void *_pSelf, va_list *pArgs){
	const char *filename = va_arg(*pArgs,char *);
	FILE *output;
	IMAGE *pSelf = _pSelf;
	BITMAPFILEHEADER  fileheader;
	BITMAPINFOHEADER infoheader;
	
	fileheader.bfType = 19778;
	fileheader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(BYTE) *3* pSelf->_.height * pSelf->_.width;
	fileheader.bfReserved1 = 0;
	fileheader.bfReserved2 = 0;
	fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	infoheader.biSize = sizeof(BITMAPINFOHEADER);
	infoheader.biWidth = pSelf->_.width;
	infoheader.biHeight = pSelf->_.height;
	infoheader.biPlanes = 1;
	infoheader.biBitCount = 24;
	infoheader.biCompression = 0;
	infoheader.biSizeImage = sizeof(BYTE) * 3 * pSelf->_.height * pSelf->_.width;
	infoheader.biXPelsPerMeter = 0;
	infoheader.biYPelsPerMeter = 0;
	infoheader.biClrUsed = 0;
	infoheader.biClrImportant = 0;

	output = fopen(filename, "wb+");
	rewind(output);
	fwrite(&fileheader, sizeof(BITMAPFILEHEADER), 1, output);
	fwrite(&infoheader, sizeof(BITMAPINFOHEADER), 1, output);
	
	CopyToFile(pSelf,output);
	fclose(output);

}

static void * ImageClass_ctor(void * _pSelf, va_list * ppArgs)
{
	
	IMAGE_CLASS * pSelf = base_ctor(ImageClass, _pSelf, ppArgs);

	

	PFUNC pfnSelector = NULL; 
	PFUNC pfnMethod = NULL;   
	va_list pArgs = * ppArgs;

	while ( 1 )
	{
		pfnSelector = va_arg( pArgs, PFUNC ); 

		if ( !pfnSelector )
			break; 

		pfnMethod = va_arg( pArgs, PFUNC );  

		if ( pfnSelector == (PFUNC) ReadImage )
			* (PFUNC *) &(pSelf->pfnReadImage) = pfnMethod;
		if ( pfnSelector == (PFUNC) WriteImage )
			* (PFUNC *) &(pSelf->pfnWriteImage) = pfnMethod;
	}

	return pSelf;
}

static OBJECT_CLASS image_class_info; 
static IMAGE_CLASS  image_info;       



const void * ImageClass = &image_class_info;
const void * Image      = &image_info;

void init_ImageClass(void)
{
	init_DdarrClass();
	inplace_new(
		&image_class_info, 
		ObjectClass,       

		"ImageClass",        
		ObjectClass,         
		sizeof(IMAGE_CLASS), 

		ctor, ImageClass_ctor, 
		0
	);

	inplace_new(
		&image_info, 
		ImageClass,  

		"Image",       
		Ddarr,        
		sizeof(IMAGE), 

		ctor, Image_ctor, 
		dtor, Image_dtor,
		ReadImage, Image_read,
		WriteImage, Image_write,

		0
	);
}

void ReadImage(const void * _pSelf, ...){
	va_list pArgs;
	const IMAGE_CLASS * pClass = classOf(_pSelf);
	assert( pClass->pfnReadImage );
	va_start( pArgs, _pSelf);
	pClass->pfnReadImage(_pSelf, &pArgs);
	va_end(pArgs);
}
	
void WriteImage(const void * _pSelf, ...){
	va_list pArgs;
	const IMAGE_CLASS * pClass = classOf(_pSelf);
	assert( pClass->pfnWriteImage );
	va_start( pArgs, _pSelf);
	pClass->pfnWriteImage(_pSelf, &pArgs);
	va_end(pArgs);
}